package com.company.gjtx.common.dao.impl;

import java.io.Serializable;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.transform.Transformers;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.util.CollectionUtils;

import com.company.gjtx.common.dao.GenericDao;
import com.company.gjtx.common.exception.DaoException;
import com.company.gjtx.common.util.PageView;

@SuppressWarnings("unchecked")
public class GenericDaoImpl<T, ID extends Serializable> implements
		GenericDao<T, ID> {

	protected static final Log logger = LogFactory.getLog(GenericDaoImpl.class);

	/**
	 * 具体的实体类型
	 */
	private Class<T> type;

	/**
	 * Spring提供的Hibernate工具类
	 */
	protected HibernateTemplate hibernateTemplate;

	/**
	 * 查询条件
	 */
	private String hql;

	/**
	 * 
	 */
	protected String EXCEPTION_MESSAGE = "数据库异常";

	public GenericDaoImpl() {
	}

	/**
	 * <p>
	 * 必须提供的构造方法,以便创建实例的时候就知道具体实体的类型
	 * <p>
	 * 
	 * @param type :
	 *            实体类型
	 */
	public GenericDaoImpl(Class<T> type) {
		this.type = type;
		this.hql = "from " + type.getName();
	}

	/**
	 * 保存实体
	 * 
	 * @param entity :
	 *            需要保存到数据库的实体
	 * @return 保存后得到的id
	 */
	public ID save(T entity) throws DaoException {
		try {
			return (ID) hibernateTemplate.save(entity);
		} catch (Exception e) {
			e.printStackTrace();
			throw new DaoException(EXCEPTION_MESSAGE);
		}
	}

	/**
	 * 保存或修改实体
	 * 
	 * @param entity :
	 *            需要保存或修改到数据库的实体
	 */
	public void saveOrUpdate(T entity) throws DaoException {
		try {
			hibernateTemplate.saveOrUpdate(entity);
		} catch (Exception e) {
			e.printStackTrace();
			throw new DaoException(EXCEPTION_MESSAGE);
		}
	}

	/**
	 * 保存或修改实体
	 * 
	 * @param entities :
	 *            需要保存或修改到数据库的实体集合
	 */
	public void saveOrUpdateAll(Collection<T> entities) throws DaoException {
		try {
			hibernateTemplate.saveOrUpdateAll(entities);
		} catch (Exception e) {
			e.printStackTrace();
			throw new DaoException(EXCEPTION_MESSAGE);
		}
	}

	public Integer updateAllByHql() throws DaoException {
		try {
			return hibernateTemplate.execute(new HibernateCallback<Integer>() {
				public Integer doInHibernate(Session session)
						throws HibernateException, SQLException {
					Query query = session.createQuery(hql);
					return query.executeUpdate();
				}
			});
		} catch (Exception e) {
			e.printStackTrace();
			throw new DaoException(EXCEPTION_MESSAGE);
		}
	}

	/**
	 * 通过设置的HQL，查找全部实体
	 * 
	 * @return 找到的所有实体的集合
	 * @throws DaoException
	 */
	public List<T> findAllByHql() throws DaoException {
		try {
			return (List<T>) hibernateTemplate.find(hql);
		} catch (Exception e) {
			e.printStackTrace();
			throw new DaoException(EXCEPTION_MESSAGE);
		}
	}

	/**
	 * 查找全部实体
	 * 
	 * @return 找到的所有实体的集合
	 */
	public List<T> findAll() throws DaoException {
		try {
			String hql = "from " + type.getName();
			return (List<T>) hibernateTemplate.find(hql);
		} catch (Exception e) {
			e.printStackTrace();
			throw new DaoException(EXCEPTION_MESSAGE);
		}
	}

	/**
	 * 通过ID查找
	 * 
	 * @param id :
	 *            实体的ID
	 * @return 找到的实体
	 */
	public T findById(ID id) throws DaoException {
		try {
			return (T) hibernateTemplate.get(type, id);
		} catch (Exception e) {
			e.printStackTrace();
			throw new DaoException(EXCEPTION_MESSAGE);
		}
	}

	/**
	 * 修改实体
	 * 
	 * @param entity :
	 *            需要从数据库更新的实体
	 */
	public void modify(T entity) throws DaoException {
		try {
			hibernateTemplate.update(entity);
		} catch (Exception e) {
			e.printStackTrace();
			throw new DaoException(EXCEPTION_MESSAGE);
		}
	}

	/**
	 * 删除实体
	 * 
	 * @param entity :
	 *            需要从数据库删除的实体
	 */
	public void remove(T entity) throws DaoException {
		try {
			hibernateTemplate.delete(entity);
		} catch (Exception e) {
			e.printStackTrace();
			throw new DaoException(EXCEPTION_MESSAGE);
		}
	}

	/**
	 * 删除实体集合
	 * 
	 * @param entities :
	 *            需要从数据库删除的实体集合
	 */
	public void removeAll(Collection<T> entities) throws DaoException {
		try {
			hibernateTemplate.deleteAll(entities);
		} catch (Exception e) {
			e.printStackTrace();
			throw new DaoException(EXCEPTION_MESSAGE);
		}
	}

	/**
	 * 计算匹配查询条件的记录总数,根据注入或者设置hql语句,获取查询语句返回数据库中所有记录
	 * 
	 * @return 记录总数
	 */
	public int getTotalRows() throws DaoException {
		try {
			String actualHql = "select count(*) "
					+ hql.substring(hql.indexOf("from"));
			List obj = this.hibernateTemplate.find(actualHql);
			if (obj != null && obj.size() > 0) {
				return ((Long) this.hibernateTemplate.find(actualHql).get(0))
						.intValue();
			}
			return 0;
		} catch (Exception e) {
			e.printStackTrace();
			throw new DaoException(EXCEPTION_MESSAGE);
		}
	}

	/**
	 * 根据每页记录的数量,计算出总的分页数
	 * 
	 * @param size
	 *            每页记录的数量
	 * @return 分页总数
	 */
	public int getPageSize(int size) throws DaoException {
		try {
			// 最大页数
			int pageSize;
			// 实际每页数据条数
			int actualSize;
			// 总记录数
			int totalRows = this.getTotalRows();
			// 计算实际每页的条数,如果请求的每页数据条数大于总条数, 则等于总条数
			actualSize = (size > totalRows) ? totalRows : size;
			if (totalRows > 0) {
				pageSize = (totalRows % size == 0) ? (totalRows / actualSize)
						: (totalRows / actualSize + 1);
			} else {
				pageSize = 0;
			}
			return pageSize;
		} catch (Exception e) {
			e.printStackTrace();
			throw new DaoException(EXCEPTION_MESSAGE);
		}
	}

	/**
	 * 根据给定的页码进行分页查找,这是纯Hibernate分页.
	 * 
	 * @param page * :
	 *            要查询的页码
	 * @param size * :
	 *            每页记录数
	 * 
	 * @return 匹配的实体列表集合
	 */
	public PageView<T> findByPage(PageView<T> pageView) throws DaoException {
		try {
			final int currtRow = pageView.getCurrPage(); // 当前第几条数据
			final int size = pageView.getRecNumPerPage();// 每页记录数
			// final int pageSize = this.getPageSize(size);//根据每页记录的数量,计算出总的分页数
			final int totalRows = this.getTotalRows();
			List<T> temp = hibernateTemplate
					.executeFind(new HibernateCallback() {
						public List<T> doInHibernate(Session session)
								throws HibernateException, SQLException {
							// 实际页码
							// int actualPage = (currtRow > pageSize) ? pageSize
							// : currtRow;
							// 计算实际每页的条数,如果请求的每页数据条数大于总条数, 则等于总条数
							int actualSize = (size > totalRows) ? totalRows
									: size;
							// 计算请求页码的第一条记录的索引值,如果
							// int startRow = (actualPage > 0) ? (actualPage -1)
							// * actualSize
							// : 0;
							Query query = session.createQuery(hql);
							// 设置第一条记录
							query.setFirstResult(currtRow);
							query.setMaxResults(actualSize);
							return (List<T>) query.list();
						}
					});
			if (CollectionUtils.isEmpty(temp)) {
				temp = new ArrayList<T>();
			}
			pageView.setResults(temp);
			pageView.setRecNum(totalRows);
			return pageView;
		} catch (Exception e) {
			e.printStackTrace();
			throw new DaoException(EXCEPTION_MESSAGE);
		}
	}

	public PageView<T> findByPageEx(PageView<T> pageView) throws DaoException {
		try {
			final int currtRow = pageView.getCurrPage(); // 当前第几条数据
			final int size = pageView.getRecNumPerPage();// 每页记录数
			// final int pageSize = this.getPageSize(size);//根据每页记录的数量,计算出总的分页数
			final int totalRows = this.getTotalRows();
			List<T> temp = hibernateTemplate
					.executeFind(new HibernateCallback() {
						public List<T> doInHibernate(Session session)
								throws HibernateException, SQLException {
							// 实际页码
							// int actualPage = (currtRow > pageSize) ? pageSize
							// : currtRow;
							// 计算实际每页的条数,如果请求的每页数据条数大于总条数, 则等于总条数
							int actualSize = (size > totalRows) ? totalRows
									: size;
							// 计算请求页码的第一条记录的索引值,如果
							// int startRow = (actualPage > 0) ? (actualPage -1)
							// * actualSize
							// : 0;
							Query query = session.createQuery(hql);
							// 设置第一条记录
							query.setFirstResult(currtRow);
							query.setMaxResults(actualSize);
							List tmpResult = query.list();
							// ArrayList<T> result=new ArrayList<T>();
							// if(tmpResult!=null&&tmpResult.size()!=0){
							// Iterator<Object[]>
							// it=(Iterator<java.lang.Object[]>)
							// tmpResult.iterator();
							// while(it.hasNext()){
							// result.add((T) it.next()[0]);
							// }
							// }
							return tmpResult;
						}
					});
			if (CollectionUtils.isEmpty(temp)) {
				temp = new ArrayList<T>();
			}
			pageView.setResults(temp);
			pageView.setRecNum(totalRows);
			return pageView;
		} catch (Exception e) {
			e.printStackTrace();
			throw new DaoException(EXCEPTION_MESSAGE);
		}
	}

	/**
	 * 因为这个类没有继承HibernateDaoSupport,所以现在由Spring注入HibernateTemplate
	 * 
	 * @param hibernateTemplate :
	 *            Spring提供的Hibernate工具类
	 */
	public void setHibernateTemplate(HibernateTemplate hibernateTemplate) {
		this.hibernateTemplate = hibernateTemplate;
	}

	public void setHql(String hql) {
		this.hql = hql;
	}

	public HibernateTemplate getHibernateTemplate() {
		return hibernateTemplate;
	}

	public String getHql() {
		return hql;
	}

	/***************************************************************************
	 * 根据SQL查询
	 * 
	 * @param sql
	 * @return
	 * @throws DaoException
	 */
	public <Object> List<Object> findBySql(final String sql)
			throws DaoException {
		try {
			return (List<Object>) getHibernateTemplate().execute(
					new HibernateCallback() {
						public Object doInHibernate(Session session)
								throws HibernateException {
							Connection con = session.connection();
							try {
								if (!con.getAutoCommit()) {
									con.commit();
									con.setAutoCommit(true);
								}
							} catch (SQLException e) {
								e.printStackTrace();
							}
							SQLQuery query = session.createSQLQuery(sql);
							List<Object> l = query.list();
							return (Object) l;
						}
					});
		} catch (Exception e) {
			e.printStackTrace();
			throw new DaoException(EXCEPTION_MESSAGE);
		}
	}

	/***************************************************************************
	 * 根据SQL 以及绑定的类查询
	 * 
	 * @param sql
	 * @param clazz
	 * @return
	 * @throws DaoException
	 */
	public <Object> List<Object> findBySql(final String sql, final Class clazz)
			throws DaoException {
		try {
			return (List<Object>) getHibernateTemplate().execute(
					new HibernateCallback() {
						public Object doInHibernate(Session session)
								throws HibernateException {
							List<Object> l = session.createSQLQuery(sql)
									.setResultTransformer(
											Transformers.aliasToBean(clazz))
									.list();
							return (Object) l;
						}
					});

		} catch (Exception e) {
			e.printStackTrace();
			throw new DaoException(EXCEPTION_MESSAGE);
		}
	}

}